/********************************************************************
	created:	2008/10/17
	created:	17:10:2008   0:19
	filename: 	h:\SimoneEngine\qianye\src\qylib\math\qyAABB.h
	author:		Xiong Jiayu
	copyright:  
	purpose:	
*********************************************************************/
#pragma once

#ifndef __QYAABB_H__
#define __QYAABB_H__

#include "../common/qyCommon.h"
#include "qyMatrix3.h"
#include "qyMatrix4.h"
#include "qyVector3.h"

//------------------------------------------------------------------------------
// struct AABB
//------------------------------------------------------------------------------
// 	            6                             7
//              ------------------------------
//             /|                           /|
//            / |                          / |
//           /  |                         /  |
//          /   |                        /   |
//         /    |                       /    |
//        /     |                      /     |
//       /      |                     /      |
//      /       |                    /       |
//     /        |                   /        |
//  2 /         |                3 /         |
//   /----------------------------/          |
//   |          |                 |          |
//   |          |                 |          |      +Y
//   |          |                 |          | 
//   |        4 |-----------------|----------|      |
//   |         /                  |         / 5     |
//   |        /                   |        /        |       +Z
//   |       /                    |       /         |
//   |      /                     |      /          |     /
//   |     /                      |     /           |    /
//   |    /                       |    /            |   /
//   |   /                        |   /             |  /
//   |  /                         |  /              | /
//   | /                          | /               |/
//   |/                           |/                ----------------- +X
//   ------------------------------
//  0                              1
//
//------------------------------------------------------------------------------
struct AABB
{
public:
	Vector3				vecs[2];

public:
						AABB( void );
	explicit			AABB( const Vector3 &min, const Vector3 &max );
	explicit			AABB( const Vector3 &point );


	const Vector3 &		operator []( const int index ) const;
	Vector3 &			operator []( const int index );
	AABB				operator +( const Vector3 &v ) const;
	AABB &				operator +=( const Vector3 &v );
	AABB				operator *( const Matrix3 &m ) const;
	AABB				operator *( const float scale ) const;
	AABB &				operator *=( const Matrix3 &m );
	AABB				operator +( const AABB &b ) const;
	AABB &				operator +=( const AABB &b );
	AABB				operator -( const AABB &b ) const;
	AABB &				operator -=( const AABB &b );

	bool				Compare( const AABB &b ) const;
	bool				operator ==( const AABB &b ) const;
	bool				operator !=( const AABB &b ) const;

	void				Zero( void );
    void                Set( const Vector3 &min, const Vector3 &max );

	Vector3				GetCenter( void ) const;
    const Vector3 &     GetMin( void ) const;
    const Vector3 &     GetMax( void ) const;
    float				GetRadius( void ) const;
	float				GetRadius( const Vector3 &v ) const;
	float				Volume( void ) const;

	bool				AddPoint( const Vector3 &v );
	bool				AddAABB( const AABB &b );
	//bool				OpVolumeAnd( const AABB &b ) const;
	AABB				Expand( const float d ) const;
	AABB &				ExpandSelf( const float d );

	const Vector3		Size( void ) const;
	float				Distance( const Vector3 &v ) const;
	bool				Contains( const AABB &b ) const;
	bool				ContainsPoint( const Vector3 &v ) const;

	float				ComputeArea( void ) const;
	float				ComputeVolume( void ) const;

	AABB				Translate( const Vector3 &v ) const;
	AABB &				TranslateSelf( const Vector3 &v );
	AABB &				TranslateTo( const Vector3 &pos );

	AABB				Transform( const Matrix3 &m ) const;
	AABB &				TransformSlef( const Matrix3 &m );

    AABB                Transform( const Matrix4 &m ) const;
    AABB &              TransformSelf( const Matrix4 &m );

	bool				LineIntersection( const Vector3 &start, const Vector3 &end ) const;
	// intersection point is start + dir * scale
	bool				RayIntersection( const Vector3 &start, const Vector3 &dir, float &scale ) const;
	bool				RayIntersection( const Vector3 &start, const Vector3 &dir, float &near, float &far ) const;
    bool			    IntersectsBounds( const AABB &a ) const;	// includes touching
};

//-----------------------------------------------------------------------
QY_INLINE AABB::AABB()
{
}

//-----------------------------------------------------------------------
QY_INLINE AABB::AABB( const Vector3 &min, const Vector3 &max )
{
	vecs[0] = min;
	vecs[1] = max;
}

//-----------------------------------------------------------------------
QY_INLINE AABB::AABB( const Vector3 &point )
{
	vecs[0] = point;
	vecs[1] = point;
}

//-----------------------------------------------------------------------
QY_INLINE Vector3 & AABB::operator []( const int index )
{
	return vecs[index];
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 & AABB::operator []( const int index ) const
{
	return vecs[index];
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::operator +( const Vector3 &v ) const
{
	return AABB( vecs[0] + v, vecs[1] + v ); 
}

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::operator +=( const Vector3 &v )
{
	vecs[0] += v;
	vecs[1] += v;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::operator +( const AABB &b ) const
{
	AABB newBox;
	newBox = *this;
	newBox.AddAABB( b );
	return newBox;
}

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::operator +=( const AABB &b )
{
	AddAABB( b );
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::operator -( const AABB &b ) const
{
	assert( vecs[1].x - vecs[0].x > b.vecs[1].x - b.vecs[0].x &&
		vecs[1].y - vecs[0].y > b.vecs[1].y - b.vecs[0].y &&
		vecs[1].z - vecs[0].z > b.vecs[1].z - b.vecs[0].z );
	return AABB( Vector3( vecs[0].x + b.vecs[1].x, vecs[0].y + b.vecs[1].y, vecs[0].z + b.vecs[1].z ),
		Vector3( vecs[1].x + b.vecs[0].x, vecs[1].y + b.vecs[0].y, vecs[1].z + b.vecs[0].z ) );
}	

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::operator -=(const AABB &b)
{
	assert( vecs[1].x - vecs[0].x > b.vecs[1].x - b.vecs[0].x &&
		vecs[1].y - vecs[0].y > b.vecs[1].y - b.vecs[0].y &&
		vecs[1].z - vecs[0].z > b.vecs[1].z - b.vecs[0].z );
	vecs[0] += b[1];
	vecs[1] += b[0];
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::Compare( const AABB &b ) const
{
	return ( (vecs[0].Compare( b.vecs[0] )) && (vecs[1].Compare( b.vecs[1] )) );
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::operator ==( const AABB &b ) const
{
	return this->Compare( b );
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::operator !=( const AABB &b ) const 
{
	return !this->Compare( b );
}

//-----------------------------------------------------------------------
QY_INLINE void AABB::Zero()
{
	vecs[0].Zero();
	vecs[1].Zero();
}

//-----------------------------------------------------------------------
QY_INLINE void AABB::Set( const Vector3 &min, const Vector3 &max )
{
    vecs[0] = min;
    vecs[1] = max;
}

//-----------------------------------------------------------------------
QY_INLINE Vector3 AABB::GetCenter( void ) const
{
	return Vector3( ( vecs[0].x + vecs[1].x ) * 0.5f,
		( vecs[0].y + vecs[1].y ) * 0.5f,
		( vecs[0].z + vecs[1].z ) * 0.5f );
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 & AABB::GetMin() const
{
    return vecs[0];
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 & AABB::GetMax() const
{
    return vecs[1];
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::GetRadius(void) const
{
	float x = (vecs[1].x - vecs[0].x ) * 0.5f;
	float y = (vecs[1].y - vecs[0].y ) * 0.5f;
	float z = (vecs[1].z - vecs[0].z ) * 0.5f;
	return sqrtf( x * x + y * y + z * z );	
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::GetRadius(const Vector3 &v) const
{
	// TODO:
	// !!
	return 0.0f;
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::Volume() const
{
	if ( vecs[0].x >= vecs[1].x || vecs[0].y >= vecs[1].y || vecs[0].z >= vecs[1].z ) {
		return 0.0f;
	}
	return ( ( vecs[1].x - vecs[0].x ) * ( vecs[1].y - vecs[0].y ) * ( vecs[1].z - vecs[0].z ) );
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::AddPoint(const Vector3 &v)
{
	/*bool expanded = false;
	if ( v[0] < vecs[0].x) {
		vecs[0].x = v[0];
		expanded = true;
	}
	if ( v[0] > vecs[1].x) {
		vecs[1].x = v[0];
		expanded = true;
	}
	if ( v[1] < vecs[0].y ) {
		vecs[0].y = v[1];
		expanded = true;
	}
	if ( v[1] > vecs[1].y) {
		vecs[1].y = v[1];
		expanded = true;
	}
	if ( v[2] < vecs[0].z ) {
		vecs[0].z = v[2];
		expanded = true;
	}
	if ( v[2] > vecs[1].z) {
		vecs[1].z = v[2];
		expanded = true;
	}
	return expanded;*/
    bool expanded = false;
    if ( v.x < vecs[0].x ) {
        vecs[0].x = v.x;
        expanded = true;
    }
    if ( v.x > vecs[1].x) {
        vecs[1].x = v.x;
        expanded = true;
    }
    if ( v.y < vecs[0].y ) {
        vecs[0].y  = v.y ;
        expanded = true;
    }
    if ( v.y  > vecs[1].y ) {
        vecs[1].y  = v.y ;
        expanded = true;
    }
    if ( v.z < vecs[0].z ) {
        vecs[0].z = v.z;
        expanded = true;
    }
    if ( v.z > vecs[1].z) {
        vecs[1].z = v.z;
        expanded = true;
    }
    return expanded;
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::AddAABB(const AABB &b)
{
	bool expanded = false;
	if ( b.vecs[0].x < vecs[0].x ) {
		vecs[0].x = b.vecs[0].x;
		expanded = true;
	}
	if ( b.vecs[0].y < vecs[0].y ) {
		vecs[0].y = b.vecs[0].y;
		expanded = true;
	}
	if ( b.vecs[0].z < vecs[0].z ) {
		vecs[0].z = b.vecs[0].z;
		expanded = true;
	}
	if ( b.vecs[1].x > vecs[1].x ) {
		vecs[1].x = b.vecs[1].x;
		expanded = true;
	}
	if ( b.vecs[1].y > vecs[1].y ) {
		vecs[1].y = b.vecs[1].y;
		expanded = true;
	}
	if ( b.vecs[1].z > vecs[1].z ) {
		vecs[1].z = b.vecs[1].z;
		expanded = true;
	}
	return expanded;
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::Expand(const float d) const
{
	return AABB( Vector3( vecs[0].x - d, vecs[0].y - d, vecs[0].z - d ),
		Vector3( vecs[1].x + d, vecs[1].y + d, vecs[1].z + d ) );
}

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::ExpandSelf(const float d)
{
	vecs[0].x -= d;
	vecs[1].x -= d;
	vecs[0].y += d;
	vecs[1].y += d;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE const Vector3 AABB::Size() const
{
	return vecs[1] - vecs[0];
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::Distance(const Vector3 &v) const
{
	int	i;
	float delta, distance;

	if( ContainsPoint( v ) ) {

		return( 0.0f );
	}

	distance = 0.0f;
	for( i = 0; i < 3; i++ ) {

		if( v[i] < vecs[0][i] ) {

			delta = vecs[0][i] - v[i];
			distance += delta * delta;

		} else if ( v[i] > vecs[1][i] ) {

			delta = v[i] - vecs[1][i];
			distance += delta * delta;
		}
	}

	return( sqrt( distance ) );
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::Contains( const AABB &a ) const
{
    if ( a[0].x < vecs[0].x || a[0].y < vecs[0].y || a[0].z < vecs[0].z ||
        a[1].x > vecs[1].x || a[1].y > vecs[1].y || a[1].z > vecs[1].z ) 
    {
            return false;
    }
    return true;
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::ContainsPoint(const Vector3 &v) const
{
	if ( v.x < vecs[0].x || v.y < vecs[0].y || v.z < vecs[0].z
	|| v.x > vecs[1].x || v.y > vecs[1].y || v.z > vecs[1].z ) {
		return false;
	}
	return true;
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::Translate(const Vector3 &v) const
{
	return AABB( Vector3( vecs[0] + v ), Vector3( vecs[1] + v ) );
}

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::TranslateSelf(const Vector3 &v)
{
	vecs[0] += v;
	vecs[1] += v;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE AABB & AABB::TranslateTo(const Vector3 &pos)
{
	Vector3 mid = ( vecs[0] + vecs[1] ) * 0.5f;
	Vector3 delta = pos - mid;
	vecs[0] += delta;
	vecs[1] += delta;
	return (*this);
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::Transform( const Matrix3 &m ) const
{
	return AABB();
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::Transform( const Matrix4 &m ) const
{
    AABB box;
    Vector3 pos;
    pos.Set( m[3].x, m[3].y, m[3].z );
    box.vecs[0] = vecs[0] + pos;;
    box.vecs[1] = vecs[1] + pos;
    return box;
}

//-----------------------------------------------------------------------
QY_INLINE bool AABB::IntersectsBounds( const AABB &a ) const
{
    /*if ( a.vecs[1].x < vecs[0].x || a.vecs[1].y < vecs[0].y || a.vecs[1].z < vecs[0].z
        || a.vecs[0].x > vecs[1].x || a.vecs[0].y > vecs[1].y || a.vecs[0].z > vecs[1].z ) 
    {
        return false;
    }
    return true;*/
    if ( a.vecs[1].x < vecs[0].x || a.vecs[1].y < vecs[0].y || a.vecs[1].z < vecs[0].z
    || a.vecs[0].x > vecs[1].x || a.vecs[0].y > vecs[1].y || a.vecs[0].z > vecs[1].z ) 
    {
        return false;
    }
    return true;
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::ComputeArea() const
{
	Vector3 d = vecs[1] - vecs[0];
	return 2 * ( d.x * d.x + d.y + d.y + d.z * d.z );
}

//-----------------------------------------------------------------------
QY_INLINE float AABB::ComputeVolume() const
{
	Vector3 d = vecs[1] - vecs[0];
	return ( d.x * d.y * d.z );
}

//-----------------------------------------------------------------------
QY_INLINE AABB AABB::operator*( const float scale ) const
{
	Vector3 extent = ( vecs[1] - vecs[0] ) * 0.5f;
	const Vector3 &center = GetCenter();
	return AABB( center - extent, center + extent );
}

#endif

